/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package model;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.SignatureException;
import java.security.cert.CRLException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bouncycastle.asn1.x509.CRLReason;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.x509.X509V2CRLGenerator;


import sun.security.x509.AlgorithmId;
import sun.security.x509.CertificateAlgorithmId;
import sun.security.x509.CertificateIssuerName;
import sun.security.x509.CertificateSerialNumber;
import sun.security.x509.CertificateSubjectName;
import sun.security.x509.CertificateValidity;
import sun.security.x509.CertificateVersion;
import sun.security.x509.CertificateX509Key;
import sun.security.x509.X500Name;
import sun.security.x509.X509CRLImpl;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo;

/**
 *
 * @author XuanTuan
 */
public class CA_Service {

    public static X509CertImpl createCert(X500Name subject, PrivateKey privateKey, PublicKey publicKey)
            throws CertificateException, IOException, InvalidKeyException,
            NoSuchAlgorithmException, NoSuchProviderException,
            SignatureException {
        // CertInfo
        X509CertInfo certInfo = new X509CertInfo();
        certInfo.set(X509CertInfo.VERSION, new CertificateVersion(2));
        certInfo.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber((int) (System.currentTimeMillis() / 1000)));
        certInfo.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid)));
        certInfo.set(X509CertInfo.ISSUER, new CertificateIssuerName(new X500Name("CN=Verisign, OU=JavaSoft, O=Sun Microsystems, C=US")));
        certInfo.set(X509CertInfo.VALIDITY, new CertificateValidity(new Date(), new Date(System.currentTimeMillis() + 365L * 24L * 60L * 60L * 1000L)));
        certInfo.set(X509CertInfo.SUBJECT, new CertificateSubjectName(subject));
        certInfo.set(X509CertInfo.KEY, new CertificateX509Key(publicKey));

        X509CertImpl certImpl = new X509CertImpl(certInfo);
        certImpl.sign(privateKey, "MD5WithRSA");
        return certImpl;
    }

    public static boolean validateCert(FileInputStream cert) {
        try {
            X509CertImpl certImpl = new X509CertImpl(cert);
            certImpl.checkValidity();
            return true;
        } catch (Exception ex) {
//            ex.printStackTrace();
        }
        return false;
    }

    public static boolean validateCert(X509CertImpl cert) {
        try {
            cert.checkValidity();
            return true;
        } catch (Exception ex) {
//            ex.printStackTrace();
        }
        return false;
    }

    public static boolean verifyCert(FileInputStream cert, PublicKey publicKey) {
        try {
            X509CertImpl certImpl = new X509CertImpl(cert);
            System.out.println(certImpl.toString());
            certImpl.verify(publicKey);
            return true;
        } catch (Exception ex) {
//            ex.printStackTrace();
        }
        return false;
    }

    public static boolean verifyCert(X509CertImpl cert, PublicKey publicKey) {
        try {
            cert.verify(publicKey);
            return true;
        } catch (Exception ex) {
//            ex.printStackTrace();
        }
        return false;
    }

    public static boolean isRevoke(FileInputStream cert, InputStream crl, PrivateKey privateKey) throws CertificateException, CRLException, NoSuchProviderException, SecurityException, SignatureException, InvalidKeyException {
        X509CertImpl certImpl = new X509CertImpl(cert);
        X509CRL x509crl = getCRL(crl, privateKey);
        return x509crl.isRevoked(certImpl);
    }

    public static boolean isRevoke(X509CertImpl cert, InputStream crl, PrivateKey privateKey) throws CertificateException, CRLException, NoSuchProviderException, SecurityException, SignatureException, InvalidKeyException {
        X509CRL x509crl = getCRL(crl, privateKey);
        return x509crl.isRevoked(cert);
    }

    public static X509CertImpl updateCert(InputStream cert, X500Name subject, PrivateKey privateKey) {
        try {
            byte[] certBytes = new byte[cert.available()];
            cert.read(certBytes);
            X509CertImpl certImpl = new X509CertImpl(certBytes);

            X509CertInfo certInfoUpdate = new X509CertInfo();
            certInfoUpdate.set(X509CertInfo.VERSION, new CertificateVersion(2));
            certInfoUpdate.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber((int) (System.currentTimeMillis() / 1000)));
            certInfoUpdate.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid)));
            certInfoUpdate.set(X509CertInfo.ISSUER, new CertificateIssuerName(new X500Name("CN=Verisign, OU=JavaSoft, O=Sun Microsystems, C=US")));
            certInfoUpdate.set(X509CertInfo.VALIDITY, new CertificateValidity(certImpl.getNotBefore(), certImpl.getNotAfter()));
            certInfoUpdate.set(X509CertInfo.SUBJECT, new CertificateSubjectName(subject));
            certInfoUpdate.set(X509CertInfo.KEY, new CertificateX509Key(certImpl.getPublicKey()));

            X509CertImpl certImplUpdate = new X509CertImpl(certInfoUpdate);
            certImplUpdate.sign(privateKey, "MD5WithRSA");
            return certImplUpdate;

        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public static X509CertInfo getCert(InputStream cert) throws IOException, CertificateException {
        byte[] certBytes = new byte[cert.available()];
        cert.read(certBytes);
        X509CertImpl certImpl = new X509CertImpl(certBytes);
        X509CertInfo certInfo = (X509CertInfo) certImpl.get(X509CertImpl.NAME + "." + X509CertImpl.INFO);
        return certInfo;
    }

    public static X500Name getSubject(InputStream cert) throws IOException, CertificateException {
        X509CertInfo certInfo = getCert(cert);
        CertificateSubjectName certificateSubjectName = (CertificateSubjectName) certInfo.get("subject");
        X500Name subject = (X500Name) certificateSubjectName.get("dname");
        return subject;
    }

    public static X509CRL revokeCert(X509CRL existingCRL, X509Certificate caCert, PrivateKey caKey) throws CRLException, NoSuchProviderException, SecurityException, SignatureException, InvalidKeyException {
        Security.addProvider(new BouncyCastleProvider());
        X509V2CRLGenerator crlGen = new X509V2CRLGenerator();
        Date now = new Date();

        crlGen.setIssuerDN(caCert.getIssuerX500Principal());
        crlGen.setThisUpdate(now);
        crlGen.setNextUpdate(new Date(now.getTime() + 100000));
        crlGen.setSignatureAlgorithm("MD5WithRSAEncryption");

        if (existingCRL != null) {
            crlGen.addCRL(existingCRL);
        }

        crlGen.addCRLEntry(caCert.getSerialNumber(), now, CRLReason.privilegeWithdrawn);
        return crlGen.generateX509CRL(caKey, "BC");
    }

    public static X509CRL getCRL(InputStream inputStream, PrivateKey caKey) throws CRLException, NoSuchProviderException, SecurityException, SignatureException, InvalidKeyException {
        Security.addProvider(new BouncyCastleProvider());
        X509CRLImpl existingCRL = new X509CRLImpl(inputStream);

        X509V2CRLGenerator crlGen = new X509V2CRLGenerator();
        crlGen.setIssuerDN(existingCRL.getIssuerX500Principal());
        crlGen.setThisUpdate(existingCRL.getThisUpdate());
        crlGen.setNextUpdate(existingCRL.getNextUpdate());
        crlGen.setSignatureAlgorithm("MD5WithRSAEncryption");
        crlGen.addCRL(existingCRL);

        return crlGen.generateX509CRL(caKey, "BC");
    }

//    public static void main(String[] args) {
//
//
//        try {
//
//
////            X509CertInfo cert = CA_Service.getCert(new FileInputStream("E:\\NguyenXuanTuan.cer"));
////            X509CertImpl certImpl = new X509CertImpl(cert);
//
////            X509CertInfo cert2 = CA_Service.getCert(new FileInputStream("E:\\MaiAnhTho.cer"));
////            X509CertImpl certImpl2 = new X509CertImpl(cert2);
//
////            X509CRL x509crl = revokeCert(getCRL(new FileInputStream("E:\\crl.crl")), certImpl, KeyIO.getPrivateKey(new FileInputStream("E:\\pri")));
////            System.out.println(x509crl.isRevoked(certImpl2));
//
////            FileOutputStream fileOutputStream = new FileOutputStream("E:\\crl.crl");
////            fileOutputStream.write(x509crl.getEncoded());
////            fileOutputStream.close();
//
////            CertificateFactory cf = CertificateFactory.getInstance("X.509");
////            X509CRL x509crl = (X509CRL) cf.generateCRL(new FileInputStream("E:\\crl.crl"));
////            System.out.println(x509crl.isRevoked(certImpl));
//
////            X509CRL x509crl = getCRL(new FileInputStream("E:\\crl.crl"), KeyIO.getPrivateKey(new FileInputStream("E:\\pri")));
////            System.out.println(x509crl.isRevoked(certImpl2));
//
//            System.out.println(verifyCert(new FileInputStream("E:\\1318915558_Le duc quyen 3.cer"),KeyIO.getPublicKey(new FileInputStream("E:\\pub"))));
//
//        } catch (Exception ex) {
//            Logger.getLogger(CA_Service.class.getName()).log(Level.SEVERE, null, ex);
//        }
//    }
}
